
#pragma once

#include "GameStd.h"
#include "Path.h"
#include "AutoObject.h"
#include "PlayerObject.h"
#include "StationaryObject.h"
#include <OgreVector3.h>


const double WANDER_RADIUS = 50;
const double WANDER_DISTANCE = 2;
const double WANDER_JITTER = 40.0;
const double WAYPOINT_SEEK_DISTANCE = 20;

class Path;

class SteeringBehaviors
{
public:
	enum SummingMethod{WEIGHTED_AVERAGE, PRIORITIZED, DITHERED};

	enum BehaviorType{
		none			= 0x00000,
		seek			= 0x00002,
		pursuit			= 0x00004,
		arrive			= 0x00008,
		interpose		= 0x00010,
		offset_pursuit	= 0x00020,
		flee			= 0x00040,
		evade			= 0x00080,
		hide			= 0x00100,
		wall_avoid		= 0x00200,
		path_following	= 0x00400,
		wander			= 0x00800,
		seperation		= 0x01000,
		alignment		= 0x02000,
		cohesion		= 0x04000,
		flocking		= 0x08000
	};

	SteeringBehaviors(IMovingObject* agent);
	~SteeringBehaviors();
	Ogre::Vector3 Calculate();
	double ForwardComponent();
	double SideComponent();
	void SetTarget(Ogre::Vector3 Target);
	void SetTargetAgent1(IMovingObject* agent);
	void SetTargetAgent2(IMovingObject* agent);
	void SetOffset(Ogre::Vector3 offset);
	Ogre::Vector3 GetOffset();
	void SetPath(std::list<Ogre::Vector3> newPath);
	void CreateRandomPath(int numOfWaypoints, int mx, int my, int cx, int cy);
	Ogre::Vector3 Force();
	void ToggleSpacePartitioning();
	bool isSpacePartitioningOn();
	void SetSummingMethod(SummingMethod sm);

	void SeekOn(IMovingObject* agent);
	void PursuitOn(IMovingObject* agent);
	void ArriveOn(IMovingObject* agent);
	void InterposeOn(IMovingObject* agent1, IMovingObject* agent2);
	void OffsetPursuitOn(IMovingObject* agent, Ogre::Vector3 newoffset);
	void FleeOn(IMovingObject* agent);
	void EvadeOn(IMovingObject* agent);
	void HideOn(IMovingObject* agent);
	void WallAvoidanceOn();
	void PathFollowingOn();
	void WanderOn();
	void SeperationOn();
	void AlignmentOn();
	void CohesionOn();
	void FlockingOn();

	void SeekOff();
	void PursuitOff();
	void ArriveOff();
	void InterposeOff();
	void OffsetPursuitOff();
	void FleeOff();
	void EvadeOff();
	void HideOff();
	void WallAvoidanceOff();
	void PathFollowingOff();
	void WanderOff();
	void SeperationOff();
	void AlignmentOff();
	void CohesionOff();
	void FlockingOff();

	bool isSeekOn();
	bool isPursuitOn();
	bool isArriveOn();
	bool isInterposeOn();
	bool isOffsetPursuitOn();
	bool isFleeOn();
	bool isEvadeOn();
	bool isHideOn();
	bool isWallAvoidanceOn();
	bool isPathFollowingOn();
	bool isWanderOn();
	bool isSeperationOn();
	bool isAlignmentOn();
	bool isCohesionOn();
	bool isFlockingOn();

	double GetDBoxLength();
	std::vector<Ogre::Vector3> GetFeelers();
	double GetWanderJitter();
	double GetWanderDistance();
	double GetWanderRadius();

	double GetSeperationWeight();
	double GetAlignmentWeight();
	double GetCohesionWeight();

private:

	//A pointer to the owner of this instance
	IMovingObject* m_Vehicle;

	Ogre::Vector3 m_SteeringForce;

	IMovingObject* m_TargetAgent1;

	IMovingObject* m_TargetAgent2;

	Ogre::Vector3 m_Target;

	std::vector<Ogre::Vector3> m_Feelers;

	double m_WallDetectionFeelerLength;

	Ogre::Vector3 m_WanderTarget;

	double m_WanderJitter;
	
	double m_WanderRadius;

	double m_WanderDistance;

	double m_WeightSeek;

	double m_WeightPursuit;

	double m_WeightArrive;

	double m_WeightInterpose;

	double m_WeightOffsetPursuit;

	double m_WeightFlee;

	double m_WeightEvade;

	double m_WeightHide;

	double m_WeightWallAvoid;

	double m_WeightPathFollowing;

	double m_WeightWander;

	double m_WeightSeperation;

	double m_WeightAlignment;

	double m_WeightCohesion;

	double m_WeightFlocking;

	double m_ViewDistance;

	Path* m_Path;

	double m_WaypointSeekDistSq;

	Ogre::Vector3 m_Offset;

	int m_Flags;

	enum Deceleration{SLOW,NORMAL,FAST};

	Deceleration m_Deceleration;

	bool m_CellSpaceOn;

	SummingMethod m_SummingMethod;

	double m_DBoxLength;

	double PanicDistanceSq;

	double ThreatRange;

	bool On(BehaviorType bt);

	bool AccumulateForce(Ogre::Vector3 &force, Ogre::Vector3 forceToAdd);

	void CreateFeelers();

	/* BEGIN BEHAVIOR DECLARATIONS */
	Ogre::Vector3 Seek(Ogre::Vector3 TargetPos);

	Ogre::Vector3 Pursuit(IMovingObject* agent);

	Ogre::Vector3 Arrive(Ogre::Vector3 TargetPos, Deceleration decel);

	Ogre::Vector3 Interpose(IMovingObject* agentA, IMovingObject* agentB);

	Ogre::Vector3 OffsetPursuit(IMovingObject* agent, Ogre::Vector3 offset);

	Ogre::Vector3 Flee(Ogre::Vector3 hunterPos);

	Ogre::Vector3 Evade(IMovingObject* agent);

	Ogre::Vector3 Hide(IMovingObject* hunter);

	Ogre::Vector3 WallAvoidance();

	Ogre::Vector3 FollowPath();

	Ogre::Vector3 Wander();

	Ogre::Vector3 Seperation();

	Ogre::Vector3 Alignment();

	Ogre::Vector3 Cohesion();
	/* END BEHAVIOR DECLARATIONS */

	Ogre::Vector3 CalculateWeightedSum();

	Ogre::Vector3 CalculatePrioritized();

	Ogre::Vector3 CalculateDithered();

	Ogre::Vector3 GetHidingPosition(Ogre::Vector3 posOb, double radiusOb, Ogre::Vector3 posHunter);

};